﻿using Inspire.Classes.Common;
using Inspire.DataStructures;
using Inspire.Entities;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Inspire.Utility;

namespace Inspire.Classes.Business
{
    public class ResourceBusiness
    {
        /// <summary>
        /// The singleton instance of ResourceBusiness.
        /// </summary>
        private static ResourceBusiness resourceBusinessSingleton = null;

        /// <summary>
        /// Reference to the CustomFieldBusiness singleton.
        /// </summary>
        private CustomFieldBusiness _customFieldBusiness = null;

        /// <summary>
        /// Static object used for locking the GetResourceBusiness() method so no two threads can
        /// call the method simultaneously (creating two instances of this class). Synchronization
        /// is required to implement the Singleton pattern correctly.
        /// </summary>
        private static object syncLock = new object();

        /// <summary>
        /// Get a reference to our persistence context.
        /// </summary>
        private InspireEntities _context;

        /// <summary>
        /// Private constructor. This is going to be a Singleton.
        /// </summary>
        private ResourceBusiness()
        {
            this._context = ContextProvider.GetContextProvider().GetContext();

            this._customFieldBusiness = CustomFieldBusiness.GetCustomFieldBusiness();
        }

        /// <summary>
        /// Method for obtaining the Singleton instance of this class.
        /// </summary>
        /// <returns>ResourceBusiness</returns>
        public static ResourceBusiness GetResourceBusiness()
        {
            lock(syncLock)
            {
                if(resourceBusinessSingleton == null)
                {
                    resourceBusinessSingleton = new ResourceBusiness();
                }
                return resourceBusinessSingleton;
            }
        }

        /// <summary>
        /// Adds a new resource to the database.
        /// </summary>
        /// <param name="newResource">New Resource to be inserted into the database.</param>
        /// <param name="customFields">Custom fields belonging to the new resource.</param>
        /// <returns>The New Resource with the ID property set.</returns>
        public Resource AddResource(Resource newResource, Collection<NewCustomFieldSource> customFields)
        {
            Resource shallowCopy = null;

            try
            {
                newResource.Id = this._context.Resources.NextId(f => f.Id);

                // Save the new object to the database
                this._context.Resources.AddObject(newResource);

                // Add the custom fields to the database
                foreach (NewCustomFieldSource nextEntry in customFields)
                {
                    this._customFieldBusiness.SaveNewCustomField(nextEntry.CustomFieldType, nextEntry.FieldValue, newResource.Id);
                }

                // Persist all Changes
                this._context.SaveChanges(System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave);
            }
            catch (Exception ex)
            {
                // Handle Save Errors
                Console.Out.WriteLine(ex.Message);
                Console.Out.WriteLine(ex.StackTrace);

                return null;
            }

            return newResource;
        }

        /// <summary>
        /// Saves the state of an existing Resource.
        /// </summary>
        /// <param name="resource">The existing Resource.</param>
        /// <param name="customFields">Custom fields to be saved.</param>
        /// <returns>The saved Resource or null if the save failed.</returns>
        public Resource SaveResource(Resource resource, Collection<NewCustomFieldSource> customFields)
        {
            try
            {
                // Save the changes -- Detach the original, then attach the revised version
                this._context.Resources.Detach((Resource)this._context.GetObjectByKey(resource.EntityKey));
                this._context.Resources.Attach(resource);
                this._context.ObjectStateManager.ChangeObjectState(resource, System.Data.EntityState.Modified);

                // Save the custom fields
                foreach (NewCustomFieldSource nextEntry in customFields)
                {
                    if (nextEntry.ExistingField == null)
                    {
                        this._customFieldBusiness.SaveNewCustomField(nextEntry.CustomFieldType, nextEntry.FieldValue, resource.Id);
                    }
                    else
                    {
                        // Update the value of the CustomField from the NewCustomFieldSource object
                        nextEntry.ExistingField.SetData(nextEntry.FieldValue);
                    }
                }

                // Persist all the changes
                this._context.SaveChanges(System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave);
            }
            catch (Exception ex)
            {
                // Handle Save errors
                Console.Out.WriteLine(ex.Message);
                Console.Out.WriteLine(ex.StackTrace);

                return null;
            }

            return resource;
        }

        /// <summary>
        /// Find a Resource by the ID.
        /// </summary>
        /// <param name="resourceId">The Resource's ID to search on.</param>
        /// <returns>(Resource) The Resource whose ID matches the one provided, or NULL if no resource is found or if more than 1 resource is found.</returns>
        public Resource GetResource(int? resourceId)
        {
            IEnumerable<Resource> resultSet;

            // Check for a null
            if (resourceId == null)
            {
                return null;
            }

            // Find the resource
            resultSet = from resources in _context.Resources
                        where resources.Id == resourceId
                        select resources;

            // If more than 1 resource is found or if no resource is found, return null
            if (resultSet.Count() != 1)
            {
                return null;
            }

            // Return the Resource
            return resultSet.ElementAt(0);
        }

        /// <summary>
        /// Returns all Resources stored in the database.
        /// </summary>
        /// <returns>List&lt;Resource&gt; containing all Resources.</Resource></returns>
        public List<Resource> GetAllResources()
        {
            return (from resources in _context.Resources
                    select resources).ToList();
        }

    }
}
